
#ifndef UTILS_VECTOR
#define UTILS_VECTOR

using Vector4f = glm::vec4;
using Vector3f = glm::vec3;
using Vector2f = glm::vec2;

using Vector4i = glm::ivec4;
using Vector3i = glm::ivec3;
using Vector2i = glm::ivec2;

using Vector4b = glm::u8vec4;
using Vector3b = glm::u8vec3;
using Vector2b = glm::u8vec2;


constexpr Vector4f ZeroVector4f = Vector4f(0.f);
constexpr Vector3f ZeroVector3f = Vector3f(0.f);
constexpr Vector2f ZeroVector2f = Vector2f(0.f);

constexpr Vector4i ZeroVector4i = Vector4i(0);
constexpr Vector3i ZeroVector3i = Vector3i(0);
constexpr Vector2i ZeroVector2i = Vector2i(0);

constexpr Vector4b ZeroVector4b = Vector4b(0);
constexpr Vector3b ZeroVector3b = Vector3b(0);
constexpr Vector2b ZeroVector2b = Vector2b(0);


constexpr double EPSILON = 1e-6;


template<int _Size, class _Type, glm::qualifier Q>
std::ostream& operator<<(std::ostream& out, const glm::vec<_Size, _Type, Q>& v) {
    std::string s;
    for (int i = 0; i < _Size; i++) {
        if (i != 0)
            out << ", ";
        out << v[i];
    }
    out << s;
    return out;
}

template<int _Size, class _Type, glm::qualifier Q>
static int MaxDimension(const glm::vec<_Size, _Type, Q>& v) {
    int maxDimension = 0;
    for (int i = 1; i < _Size; i++)
        if (v[maxDimension] < v[i])
            maxDimension = i;
    return maxDimension;
}

template<int _Size, class _Type, glm::qualifier Q>
static int MinDimension(const glm::vec<_Size, _Type, Q>& v) {
    int maxDimension = 0;
    for (int i = 1; i < _Size; i++)
        if (v[maxDimension] > v[i])
            maxDimension = i;
    return maxDimension;
}

template<int _Size, class _Type, glm::qualifier Q>
static glm::vec<_Size, _Type, Q> MaxEachComponent(const glm::vec<_Size, _Type, Q>& v1, const glm::vec<_Size, _Type, Q>& v2) {
    glm::vec<_Size, _Type, Q> ret;
    for (int i = 0; i < _Size; i++)
        ret[i] = std::max(v1[i], v2[i]);
    return ret;
}

template<int _Size, class _Type, glm::qualifier Q, typename... Args>
static glm::vec<_Size, _Type, Q> MaxEachComponent(const glm::vec<_Size, _Type, Q>& v1, const glm::vec<_Size, _Type, Q>& v2, Args... args) {
    glm::vec<_Size, _Type, Q> ret = MaxEachComponent(v1, v2);
    return MaxEachComponent(ret, args...);
}

template<int _Size, class _Type, glm::qualifier Q>
static glm::vec<_Size, _Type, Q> MinEachComponent(const glm::vec<_Size, _Type, Q>& v1, const glm::vec<_Size, _Type, Q>& v2) {
    glm::vec<_Size, _Type, Q> ret;
    for (int i = 0; i < _Size; i++)
        ret[i] = std::min(v1[i], v2[i]);
    return ret;
}

template<int _Size, class _Type, glm::qualifier Q, typename... Args>
static glm::vec<_Size, _Type, Q> MinEachComponent(const glm::vec<_Size, _Type, Q>& v1, const glm::vec<_Size, _Type, Q>& v2, Args... args) {
    glm::vec<_Size, _Type, Q> ret = MinEachComponent(v1, v2);
    return MinEachComponent(ret, args...);
}


template<int _Size, class _Type, glm::qualifier Q>
static _Type MaxComponent(const glm::vec<_Size, _Type, Q>& v) {
    _Type maxValue = std::abs(v[0]);
    for (int i = 0; i < _Size; i++)
        if (maxValue < abs(v[i]))
            maxValue = abs(v[i]);
    return maxValue;
}

template<int _Size, class _Type, glm::qualifier Q>
static _Type MinComponent(const glm::vec<_Size, _Type, Q>& v) {
    _Type minValue = abs(v[0]);
    for (int i = 0; i < _Size; i++)
        if (minValue > abs(v[i]))
            minValue = abs(v[i]);
    return minValue;
}


template<class _Type, glm::qualifier Q>
static glm::vec<2, _Type, Q> LinearInterpolate(const glm::vec<2, _Type, Q>& v1, const glm::vec<2, _Type, Q>& v2, const float t) {
    return (1 - t) * v1 + t * v2;
}

template<int _Size, class _Type, glm::qualifier Q>
static glm::vec<_Size + 1, _Type, Q> Fill(const glm::vec<_Size, _Type, Q>& v, const _Type& value) {
    glm::vec<_Size + 1, _Type, Q> ret;
    for (int i = 0; i < _Size; i++)
        ret[i] = v[i];
    ret[_Size] = value;
    return ret;
}

template<int _Size, class _Type, glm::qualifier Q>
static glm::vec<_Size - 1, _Type, Q> Eliminate(const glm::vec<_Size, _Type, Q>& v) {
    glm::vec<_Size - 1, _Type, Q> ret;
    for (int i = 0; i < _Size - 1; i++)
        ret[i] = v[i];
    return ret;
}


template<int _Size, class _Type, glm::qualifier Q>
static glm::vec<_Size, _Type, Q> Normalize(const glm::vec<_Size, _Type, Q>& v) {
    return glm::normalize(v);
}


template<int _Size, class _Type, glm::qualifier Q>
static glm::vec<_Size, _Type, Q> CrossProduct(const glm::vec<_Size, _Type, Q>& v1, const glm::vec<_Size, _Type, Q>& v2) {
    return glm::cross(v1, v2);
}


template<int _Size, class _Type, glm::qualifier Q>
static _Type DotProduct(const glm::vec<_Size, _Type, Q>& v1, const glm::vec<_Size, _Type, Q>& v2) {
    return glm::dot(v1, v2);
}

#endif // UTILS_VECTOR
